"""
页面处理器基类

提供页面操作的基础功能和通用方法
"""

import asyncio
import sys
from abc import ABC, abstractmethod
from typing import Optional, Dict, Any, List
from playwright.async_api import Page
from pathlib import Path

# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))

try:
    from src.utils.logger import get_logger
    from src.exceptions.exceptions import PageElementException, PopupException
except ImportError:
    try:
        from utils.logger import get_logger
        from exceptions.exceptions import PageElementException, PopupException
    except ImportError:
        from utils.logger import get_logger
        from exceptions.exceptions import PageElementException, PopupException

logger = get_logger(__name__)


class BasePageHandler(ABC):
    """页面处理器基类"""
    
    def __init__(self, page: Page):
        """
        初始化页面处理器
        
        Args:
            page: Playwright页面对象
        """
        self.page = page
        self.logger = get_logger(self.__class__.__name__)
    
    @abstractmethod
    async def handle(self) -> Dict[str, Any]:
        """
        处理页面的抽象方法
        
        Returns:
            Dict: 处理结果
        """
        pass
    
    async def wait_for_page_load(self, timeout: int = 30000) -> bool:
        """
        等待页面加载完成
        
        Args:
            timeout: 超时时间（毫秒）
            
        Returns:
            bool: 是否加载成功
        """
        try:
            await self.page.wait_for_load_state("networkidle", timeout=timeout)
            self.logger.info("页面加载完成")
            return True
        except Exception as e:
            self.logger.warning(f"页面加载超时: {str(e)}")
            return False
    
    async def check_for_popups(self, timeout: int = 5000) -> List[Dict[str, Any]]:
        """
        检查页面中的弹窗
        
        Args:
            timeout: 检查超时时间（毫秒）
            
        Returns:
            List: 发现的弹窗信息列表
        """
        popups = []
        
        # 常见弹窗选择器
        popup_selectors = [
            # 模态对话框
            ".modal",
            ".dialog",
            ".popup",
            ".overlay",
            # 广告弹窗
            ".ad-popup",
            ".advertisement",
            ".ad-modal",
            # 通知弹窗
            ".notification",
            ".alert",
            ".toast",
            # 确认对话框
            ".confirm-dialog",
            ".warning-dialog",
            # 自定义弹窗
            "[role='dialog']",
            "[role='alertdialog']",
            # 常见class名称
            ".popup-container",
            ".modal-container",
            ".dialog-container"
        ]
        
        for selector in popup_selectors:
            try:
                elements = await self.page.query_selector_all(selector)
                for element in elements:
                    # 检查元素是否可见
                    is_visible = await element.is_visible()
                    if is_visible:
                        # 获取弹窗信息
                        popup_info = await self._analyze_popup(element, selector)
                        if popup_info:
                            popups.append(popup_info)
                            
            except Exception as e:
                self.logger.debug(f"检查弹窗选择器 {selector} 时出错: {str(e)}")
        
        if popups:
            self.logger.info(f"发现 {len(popups)} 个弹窗")
        
        return popups
    
    async def _analyze_popup(self, element, selector: str) -> Optional[Dict[str, Any]]:
        """
        分析弹窗元素
        
        Args:
            element: 弹窗元素
            selector: 选择器
            
        Returns:
            Dict: 弹窗信息
        """
        try:
            # 获取弹窗文本内容
            text_content = await element.text_content()
            
            # 查找关闭按钮
            close_buttons = await element.query_selector_all(
                "button:has-text('关闭'), button:has-text('取消'), button:has-text('×'), "
                ".close, .close-btn, [aria-label*='close'], [aria-label*='关闭']"
            )
            
            # 获取弹窗位置和大小
            bounding_box = await element.bounding_box()
            
            popup_info = {
                "selector": selector,
                "text_content": text_content[:200] if text_content else "",  # 限制文本长度
                "has_close_button": len(close_buttons) > 0,
                "close_button_count": len(close_buttons),
                "position": bounding_box,
                "element": element
            }
            
            return popup_info
            
        except Exception as e:
            self.logger.debug(f"分析弹窗元素失败: {str(e)}")
            return None
    
    async def close_popup(self, popup_info: Dict[str, Any]) -> bool:
        """
        关闭弹窗
        
        Args:
            popup_info: 弹窗信息
            
        Returns:
            bool: 是否成功关闭
        """
        try:
            element = popup_info["element"]
            
            # 尝试多种关闭方式
            close_methods = [
                # 点击关闭按钮
                ("button:has-text('关闭')", "关闭按钮"),
                ("button:has-text('取消')", "取消按钮"),
                ("button:has-text('×')", "×按钮"),
                (".close", "关闭图标"),
                (".close-btn", "关闭按钮"),
                ("[aria-label*='close']", "关闭标签"),
                ("[aria-label*='关闭']", "关闭标签"),
                # 点击遮罩层（如果存在）
                (".modal-backdrop", "遮罩层"),
                (".overlay", "覆盖层")
            ]
            
            for selector, description in close_methods:
                try:
                    close_element = await element.query_selector(selector)
                    if close_element:
                        await close_element.click()
                        self.logger.info(f"通过{description}关闭弹窗")
                        
                        # 等待弹窗消失
                        await asyncio.sleep(0.5)
                        is_visible = await element.is_visible()
                        if not is_visible:
                            return True
                            
                except Exception as e:
                    self.logger.debug(f"尝试{description}关闭失败: {str(e)}")
            
            # 如果所有方法都失败，尝试按ESC键
            try:
                await self.page.keyboard.press("Escape")
                self.logger.info("尝试按ESC键关闭弹窗")
                await asyncio.sleep(0.5)
                is_visible = await element.is_visible()
                if not is_visible:
                    return True
            except Exception as e:
                self.logger.debug(f"按ESC键关闭失败: {str(e)}")
            
            self.logger.warning("无法关闭弹窗")
            return False
            
        except Exception as e:
            self.logger.error(f"关闭弹窗时发生错误: {str(e)}")
            return False
    
    async def close_all_popups(self) -> int:
        """
        关闭所有弹窗
        
        Returns:
            int: 成功关闭的弹窗数量
        """
        popups = await self.check_for_popups()
        closed_count = 0
        
        for popup in popups:
            try:
                if await self.close_popup(popup):
                    closed_count += 1
                    # 等待一下再处理下一个弹窗
                    await asyncio.sleep(0.5)
            except Exception as e:
                self.logger.error(f"关闭弹窗失败: {str(e)}")
        
        if closed_count > 0:
            self.logger.info(f"成功关闭 {closed_count} 个弹窗")
        
        return closed_count
    
    async def find_element_by_text(self, text: str, tag: str = "*") -> Optional[Any]:
        """
        根据文本内容查找元素
        
        Args:
            text: 要查找的文本
            tag: 标签类型，默认为所有标签
            
        Returns:
            元素对象或None
        """
        try:
            # 尝试精确匹配
            selector = f"{tag}:has-text('{text}')"
            element = await self.page.query_selector(selector)
            
            if not element:
                # 尝试部分匹配
                elements = await self.page.query_selector_all(tag)
                for elem in elements:
                    elem_text = await elem.text_content()
                    if elem_text and text.lower() in elem_text.lower():
                        element = elem
                        break
            
            return element
            
        except Exception as e:
            self.logger.debug(f"查找文本元素失败: {str(e)}")
            return None
    
    async def wait_for_any_element(self, selectors: List[str], timeout: int = 10000) -> Optional[str]:
        """
        等待任意一个元素出现
        
        Args:
            selectors: 选择器列表
            timeout: 超时时间（毫秒）
            
        Returns:
            str: 首先出现的元素选择器，如果都没出现则返回None
        """
        try:
            # 创建等待任务
            tasks = []
            for selector in selectors:
                task = asyncio.create_task(
                    self.page.wait_for_selector(selector, timeout=timeout)
                )
                tasks.append((task, selector))
            
            # 等待第一个完成的任务
            done, pending = await asyncio.wait(
                [task for task, _ in tasks],
                return_when=asyncio.FIRST_COMPLETED,
                timeout=timeout / 1000
            )
            
            # 取消未完成的任务
            for task in pending:
                task.cancel()
            
            # 返回第一个成功的选择器
            for task, selector in tasks:
                if task in done and not task.exception():
                    return selector
            
            return None
            
        except Exception as e:
            self.logger.debug(f"等待元素失败: {str(e)}")
            return None
    
    async def get_page_info(self) -> Dict[str, Any]:
        """
        获取页面基本信息
        
        Returns:
            Dict: 页面信息
        """
        try:
            return {
                "url": self.page.url,
                "title": await self.page.title(),
                "viewport": self.page.viewport_size,
                "user_agent": await self.page.evaluate("navigator.userAgent")
            }
        except Exception as e:
            self.logger.error(f"获取页面信息失败: {str(e)}")
            return {}
    
    async def scroll_to_element(self, selector: str) -> bool:
        """
        滚动到指定元素
        
        Args:
            selector: 元素选择器
            
        Returns:
            bool: 是否成功滚动
        """
        try:
            element = await self.page.query_selector(selector)
            if element:
                await element.scroll_into_view_if_needed()
                return True
            return False
        except Exception as e:
            self.logger.debug(f"滚动到元素失败: {str(e)}")
            return False